package com.zd.core.dao.template;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.util.Assert;

import com.zd.core.dao.annotation.Column;
import com.zd.core.dao.annotation.Table;
import com.zd.core.dao.entity.Page;
import com.zd.core.dao.util.DbUtil;

/**
 * jdbc操作类，可以继承、可实例化
 * @author 张迪 
 * 2011-8-29 下午03:51:16
 * @param <T>
 */
//@SuppressWarnings("unchecked")
public class JdbcOperater<T> {
	private Logger logger = LoggerFactory.getLogger(JdbcOperater.class);

	private Class<T> entityClass;
	private BaseJdbcTemplate<T> baseJdbcTemplate;
	private SimpleJdbcInsert simpleJdbcInsert;
	private String tableName;
	private Select<T> select;
	
	/**
	 * 单主键删除SQL语句
	 */
	private String simplePkDelSQL = null; 
	
	/**
	 * 只能继承使用，否则会出错。
	 * @param dataSource
	 */
	
	public JdbcOperater() {
		entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 
	}
	
	@Resource
	public void setJdbcTemplate(BaseJdbcTemplate<T> baseJdbcTemplate) {
		this.baseJdbcTemplate = baseJdbcTemplate;
		init(entityClass);
	}
	
	/**
	 * 实例化时使用
	 * @param entityClass
	 * @param baseJdbcTemplate
	 */
	public JdbcOperater(Class<T> entityClass, BaseJdbcTemplate<T> baseJdbcTemplate) {
		this.entityClass = entityClass; 
		this.baseJdbcTemplate = baseJdbcTemplate;
		init(entityClass);
	}
	
	/**
	 * 初始化
	 * 判断是否是实体、设置表名、设置jdbcInsert主键、设置单主键实体的SQL语句
	 * @param entityClass
	 */
	private void init(Class entityClass) {
		select = new Select<T>();
		Table annTable = (Table) entityClass.getAnnotation(Table.class);
		Assert.notNull(annTable, entityClass.getName() + "不是实体");
		tableName = annTable.value();
		//如果没有设置默认值则表明是类名
		if(tableName.length() == 0) {
			tableName = entityClass.getName().toLowerCase();
		}
		ArrayList<String> primaryKeys = new ArrayList<String>();
		for (Field field : entityClass.getFields()) {
			Column annColumn = field.getAnnotation(Column.class);
			if(annColumn != null && annColumn.primaryKey()) {
				primaryKeys.add(DbUtil.upperCaseToUnderline(field.getName()));
			}
		}
		simpleJdbcInsert = new SimpleJdbcInsert(baseJdbcTemplate.getSpringJdbcTemplate());
		simpleJdbcInsert.withTableName(tableName).usingGeneratedKeyColumns(primaryKeys.toArray(new String[0]));
		
		//单主键实体可以生成固定格式删除SQL语句
		if(primaryKeys.size() == 1) {
			simplePkDelSQL = "DELETE FROM " + tableName + " WHERE " + primaryKeys.get(0) + "=?"; 
		}
	}
	
	/**
	 * 保存(返回自增主键)
	 * @param entity
	 * @return 自增主键
	 */
	public Long _save(T entity, Class entityClass) {
		Table annTable = (Table)entityClass.getAnnotation(Table.class);
		Assert.notNull(annTable, entityClass.getName() + "不是实体");
		
		HashMap<String, Object> map = new HashMap<String, Object>();
		//获取所有方法
		Method[] methods = entityClass.getMethods();
		for (Field field : entityClass.getDeclaredFields()) {
			Column annColumn = field.getAnnotation(Column.class);
			if(annColumn != null) {
				//自增字段不进行插入
				if(!annColumn.identity()) {
					try {
						map.put(DbUtil.upperCaseToUnderline(field.getName()), getValue(entity, field, methods));
					} catch (Exception e) {
						throw new RuntimeException(e.getMessage());
					}
				}
			}
		}
		Number identityPK = simpleJdbcInsert.executeAndReturnKey(map);
		return identityPK.longValue();
	}
	
	/**
	 * 保存(无自增主键返回)
	 * @param entity
	 * @param entityClass
	 * @return 插入数量
	 */
	public int save(T entity) {
		Table annTable = (Table)entityClass.getAnnotation(Table.class);
		Assert.notNull(annTable, entityClass.getName() + "不是实体");
//		String tableName = annTable.value();
		StringBuilder sqlBegin = new StringBuilder("INSERT INTO " + tableName + "(");
		StringBuilder sqlEnd = new StringBuilder(")VALUES(");
		//sql语句中？对应的 参数值
		ArrayList<Object> results = new ArrayList<Object>();
		//获取所有方法
		Method[] methods = entityClass.getMethods();
		for (Field field : entityClass.getDeclaredFields()) {
			Column annColumn = field.getAnnotation(Column.class);
			if(annColumn != null) {
				//自增字段不进行插入
				if(!annColumn.identity()) {
					try {
						results.add(getValue(entity, field, methods));
						sqlBegin.append(DbUtil.upperCaseToUnderline(field.getName()) + ",");
						sqlEnd.append("?,");
					} catch (Exception e) {
						throw new RuntimeException(e.getMessage());
					}
				}
			}
		}
		sqlBegin.delete(sqlBegin.length() - 1, sqlBegin.length()).append(sqlEnd.delete(sqlEnd.length() - 1, sqlEnd.length()).append(")"));
		
		logger.debug(sqlBegin.toString());
		logger.debug("values-参数 " + Arrays.toString(results.toArray()));
		return baseJdbcTemplate.getSpringJdbcTemplate().update(sqlBegin.toString(), results.toArray());
	}
	
	/**
	 * 更新
	 * @param entity 实体
	 * @return
	 */
	public int update(T entity) {
		Table annTable = (Table)entityClass.getAnnotation(Table.class);
		Assert.notNull(annTable, entityClass.getName() + "不是实体");
//		String tableName = annTable.value();
		StringBuilder sqlBegin = new StringBuilder("UPDATE " + tableName + " SET ");
		StringBuilder sqlEnd = new StringBuilder(" WHERE ");
		
		ArrayList<Object> sets = new ArrayList<Object>();
		ArrayList<Object> wheres = new ArrayList<Object>();
		//获取所有方法
		Method[] methods = entityClass.getMethods();
		for (Field field : entityClass.getDeclaredFields()) {
			Column annColumn = field.getAnnotation(Column.class);
			if(annColumn != null) {
				//如果是主键，则作为Where条件记录
				if(annColumn.primaryKey()) {
					sqlEnd.append(field.getName() + "=? and ");
					try {
						wheres.add(getValue(entity, field, methods));
					} catch (Exception e) {
						throw new RuntimeException(e.getMessage());
					}
					//自增字段不进行更新
				} else if(!annColumn.identity()) {
					try {
						sets.add(getValue(entity, field, methods));
						sqlBegin.append(DbUtil.upperCaseToUnderline(field.getName()) + "=?,");
					} catch (Exception e) {
						throw new RuntimeException(e.getMessage());
					}
				}
				
			}
		}
		sqlBegin.delete(sqlBegin.length() - 1, sqlBegin.length()).append(sqlEnd.delete(sqlEnd.length() - 4, sqlEnd.length()));
		logger.debug(sqlBegin.toString());
		logger.debug("SET-参数 " + Arrays.toString(sets.toArray()));
		logger.debug("WHERE-参数 " + Arrays.toString(wheres.toArray()));
		return baseJdbcTemplate.getSpringJdbcTemplate().update(sqlBegin.toString(), sets.toArray(), wheres.toArray());
	}

	
	/**
	 * 多主键删除
	 * @param pks
	 * @return
	 */
	public int delete(String tableName, Map<String, Object> pks) {
		StringBuilder sql = new StringBuilder("DELETE FROM " + tableName + " WHERE ");
		ArrayList<Object> wheres = new ArrayList<Object>();
		for (Map.Entry<String, Object> pk : pks.entrySet()) {
			sql.append(pk.getKey() + "=?,");
			wheres.add(pk.getValue());
		}
		sql.delete(sql.length() - 1, sql.length());
		logger.debug(sql.toString());
		logger.debug("WHERE-参数" + wheres.toArray());
		return baseJdbcTemplate.getSpringJdbcTemplate().update(sql.toString(), wheres.toArray());
	}
	
	/**
	 * 删除
	 * @param entity 实体
	 * @return
	 */
	public int delete(T entity) {
		Table annTable = (Table)entityClass.getAnnotation(Table.class);
		Assert.notNull(annTable, entityClass.getName() + "不是实体");
//		String tableName = annTable.value();
		StringBuilder sqlBegin = new StringBuilder("DELETE FROM " + tableName + " WHERE ");
		ArrayList<Object> wheres = new ArrayList<Object>();
		Method[] methods = entityClass.getMethods();
		for (Field field : entityClass.getDeclaredFields()) {
			Column annColumn = field.getAnnotation(Column.class);
			if(annColumn != null) {
				if(annColumn.primaryKey()) {
					sqlBegin.append(field.getName() + "=? and ");
					try {
						wheres.add(getValue(entity, field, methods));
					} catch (Exception e) {
						throw new RuntimeException(e.getMessage());
					}
				} 
			}
		}
		sqlBegin.delete(sqlBegin.length() - 4, sqlBegin.length());
		logger.debug(sqlBegin.toString());
		logger.debug("WHERE-参数 " + Arrays.toString(wheres.toArray()));
		return baseJdbcTemplate.getSpringJdbcTemplate().update(sqlBegin.toString(), wheres.toArray());
	}
	
	/**
	 * 单、多个记录删除(单主键实体)
	 * @param pk 主键 
	 * @return 删除数量
	 */
	public int deleteBySimplePk(Object... pk) {
		Assert.notNull(simplePkDelSQL, "不是单主键实体，无法使用此方法");
		int count = 0;
		for (Object object : pk) {			
			count += baseJdbcTemplate.getSpringJdbcTemplate().update(simplePkDelSQL, pk);
		}
		return count;
	}

	/**
	 * 获取属性值
	 * @param entity
	 * @param methods
	 * @param field
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private Object getValue(Object entity, Field field, Method[] methods) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		boolean isGetValue = false;
		Object result = null;
		String upFieldName = field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
		String getMethod = "get" + upFieldName;
		//对布尔类型属性的特殊处理
		if(field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
			String isMethod = "is" + upFieldName;
			for (Method method : methods) {
				if(isMethod.equals(method.getName()) && getMethod.equals(method.getName())) {
					result = method.invoke(entity);
					isGetValue = true;
					break;
				}
			}
		} else {
			for (Method method : methods) {
				if(getMethod.equals(method.getName())) {
					result = method.invoke(entity);
					isGetValue = true;
					break;
				}
			}
		}
		if(!isGetValue) {
			field.setAccessible(true);
			result = field.get(entity);
		}
		return result;
	}
	
	public BaseJdbcTemplate<T> getJdbcTemplate() {
		return baseJdbcTemplate;
	}
	
	public Select<T> select() {
		return select;
	}
	
	/**
	 * 查询操作
	 * @author 张迪 
	 * 2011-8-30 上午10:12:14
	 * @param <T>
	 */
	public class Select<T> {

		public Page<T> findListBeanByPage(Page page, String sql, Object... values) {
			return (Page<T>) baseJdbcTemplate.findListBeanByPage(page, sql, entityClass, values);
		}
		
		public List<T> findListBean(String sql, Object... values) {
			return (List<T>) baseJdbcTemplate.findListBean(sql, entityClass, values);
		}
	}

}
